using Jupiter.Infrastructure;
using Jupiter.Infrastructure.Attributes;
using Jupiter.Infrastructure.Cache.Interfaces;
using Jupiter.Infrastructure.Domain.SqlSugar;
using Jupiter.Infrastructure.Exceptions;
using Jupiter.Infrastructure.Permission;
using Jupiter.Infrastructure.Permission.DataPermissions;
using Jupiter.Infrastructure.Responses;
using Microsoft.Extensions.Configuration;
using Wips.AppService.Authorities.Organizations;
using Wips.AppService.Authorities.Organizations.Requests;
using Wips.AppService.Authorities.Organizations.Responses;
using Wips.AppService.CommonViewModels;
using Wips.Domain.Models.Authorities.Model;

namespace Wips.AppService.SqlSugar.Authorities.Organizations
{
    /// <summary>
    /// 组织架构服务
    /// </summary>
    [ServiceComponent]
    public class OrganizationService : BaseQueryAppService<Organization>, IOrganizationService
    {
        //地球半径，单位米
        private const double EARTH_RADIUS = 6378137;


        /// <summary>
        /// 读取分页列表
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<PageResult<GetOrganizationPagesResponse>> GetPagesAsync(
            GetOrganizationPagesRequest request)
        {
            var result = await QueryNoTracking()
                .HasWhere(request.Keyword, p =>
                    p.Name.Contains(request.Keyword!) ||
                    p.ContactName.Contains(request.Keyword!) ||
                    p.ContactPhoneNumber.Contains(request.Keyword!))
                .HasWhere(request.Filter.Status != null,
                    p => request.Filter.Status!
                        .Select(e => (Status)Convert.ToInt32(e))
                        .Contains(p.Status))
                .Select(p => new GetOrganizationPagesResponse
                {
                    Id = p.Id!,
                    ParentId = p.ParentId,
                    Name = p.Name,
                    ContactName = p.ContactName,
                    ContactPhoneNumber = p.ContactPhoneNumber,
                    Remarks = p.Remarks,
                    Status = p.Status,
                    Created = p.Created,
                    Updated = p.Updated,
                    CreatorId = p.CreatorId
                })
                .ToPageAsync(request);

            return result;
        }


        /// <summary>
        /// 读取列表
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<PageResult<GetOrganizationPagesResponse>> GetListAsync(
            GetOrganizationPagesRequest request)
        {
            var result = await QueryNoTracking()
                .LeftJoin<Organization>((o, p) => o.ParentId == p.Id)
                .WhereIF(request.Filter.Status != null,
                    (o, p) => request.Filter.Status!
                        .Select(e => (Status)Convert.ToInt32(e))
                        .Contains(o.Status))
                .Select((o, p) =>
                    new GetOrganizationPagesResponse
                    {
                        Id = o.Id,
                        ParentId = o.ParentId,
                        ParentName = p.Name,
                        Name = o.Name,
                        ContactName = o.ContactName,
                        ContactPhoneNumber =
                            o.ContactPhoneNumber,
                        Remarks = o.Remarks,
                        Status = o.Status,
                        Created = o.Created,
                        Updated = o.Updated,
                    })
                .ToChildListAsync(it => it.ParentId, request.ParentId);
            result = result
                .WhereIF(!string.IsNullOrEmpty(request.Keyword), o =>
                    (o.Name.Contains(request.Keyword!)) ||
                    (o.ContactName != null && o.ContactName.Contains(request.Keyword!)) ||
                    (o.ContactPhoneNumber != null && o.ContactPhoneNumber.Contains(request.Keyword!)))
                .ToList();
            var response = new PageResult<GetOrganizationPagesResponse>()
            {
                TotalItems = result.Count,
                CurrentPage = 1,
                TotalPages = 1,
                Items = result,
                ItemsPerPage = 1
            };
            return response;
        }

        /// <summary>
        /// 读取信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<GetOrganizationByIdResponse> GetByIdAsync(string id)
        {
            var result = await QueryNoTracking()
                .Where(p => p.Id == id)
                .Select(p => new GetOrganizationByIdResponse
                {
                    Id = p.Id!,
                    ParentId = p.ParentId,
                    Name = p.Name,
                    ContactName = p.ContactName,
                    ContactPhoneNumber = p.ContactPhoneNumber,
                    Remarks = p.Remarks,
                    Status = p.Status
                })
                .FirstOrDefaultAsync();

            // 组织架构角色
            var roleIds = await QueryNoTracking<OrganizationRole>()
                .Where(p => p.OrganizationId == id)
                .Select(p => p.RoleId).ToListAsync();

            result.RoleIds.AddRange(roleIds);

            return result;
        }

        /// <summary>
        /// 读取树形数据
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<List<GetOrganizationTreeDataResponse>> GetTreeDataAsync(
            GetOrganizationTreeDataRequest request)
        {
            var list = await QueryNoTracking()
                .HasWhere(request.Filter.Status != null,
                    p => request.Filter.Status!
                        .Select(e => (Status)Convert.ToInt32(e))
                        .Contains(p.Status))
                // 供内部调用，只查询部分组织架构
                .HasWhere(request.InOrganization != null, n => request.InOrganization!.Contains(n.Id!))
                .ToListAsync();

            var result = new List<GetOrganizationTreeDataResponse>();
            // 递归读取
            GetTreeChildren(list, result, "");
            return result;
        }


        #region 部门招聘入职情况展示数据 (部门下拉列表)

        /// <summary>
        /// 获取组织架构列表
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<List<GetOrganizationTreeDataResponse>> GetOrganizationListResponse(
            GetOrganizationTreeDataRequest request)
        {
            var myOrganization = QueryNoTracking<OrganizationUser>()
                .Where(c => c.UserId == UserId)
                .Select(c => c.OrganizationId);
            var childOrganization = GetTreeOrganizationIds(myOrganization!);
            var list = await QueryNoTracking()
                .HasWhere(request.Filter.Status != null,
                    p => request.Filter.Status!
                        .Select(e => (Status)Convert.ToInt32(e))
                        .Contains(p.Status))
                .Where(n => childOrganization.Contains(n.Id))
                .ToListAsync();

            var result = new List<GetOrganizationTreeDataResponse>();
            // 递归读取
            GetTreeChildren(list, result, "");
            return result;
        }

        #endregion

        /// <summary>
        /// 读取树形选择框数据列表
        /// </summary>
        /// <returns></returns>
        public async Task<List<TreeSelectViewModel>> GetTreeSelectDataAsync()
        {
            var list = await QueryNoTracking().ToListAsync();

            var result = new List<TreeSelectViewModel>();
            // 递归读取
            GetTreeChildrenBySelect(list, result, "");
            return result;
        }

        /// <summary>
        /// 获取全部组织架构人员树
        /// </summary>
        /// <returns></returns>
        public async Task<List<CascaderSelectViewModel>> GetCascaderSelectDataAsync()
        {
            var list = await QueryNoTracking().ToListAsync();

            var result = new List<CascaderSelectViewModel>();
            // 递归读取
            GetTreeChildrenCascader(list, result, "");
            return result;
        }


        /// <summary>
        /// 获取下属的组织架构人员树
        /// </summary>
        /// <returns></returns>
        public async Task<List<CascaderSelectViewModel>> GetSubordinateSelectDataAsync()
        {
            var myOrganization = QueryNoTracking<OrganizationUser>()
                .Where(c => c.UserId == UserId)
                .Select(c => c.OrganizationId);
            var childOrganization = GetTreeOrganizationIds(myOrganization!);
            var list = await QueryNoTracking()
                .Where(n => childOrganization.Contains(n.Id))
                .ToListAsync();

            var result = new List<CascaderSelectViewModel>();
            // 递归读取
            GetTreeChildrenSubordinate(list, result, myOrganization, "");
            return result;
        }

        #region 部门招聘入职情况展示数据 (部门下拉列表)

        /// <summary>
        /// 获取组织架构列表
        /// </summary>
        /// <returns></returns>
        public async Task<List<TreeSelectViewModel>> GetOrganizationListAsync()
        {
            var myOrganization = QueryNoTracking<OrganizationUser>()
                .Where(c => c.UserId == UserId)
                .Select(c => c.OrganizationId);
            var childOrganization = GetTreeOrganizationIds(myOrganization!);
            var list = await QueryNoTracking()
                .Where(n => childOrganization.Contains(n.Id))
                .ToListAsync();

            var result = new List<TreeSelectViewModel>();
            // 递归读取
            GetTreeChildrenBySelect(list, result, "");
            return result;
        }

        #endregion

        #region 私有方法

        /// <summary>
        /// 递归读取
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="results"></param>
        /// <param name="parentId"></param>
        private void GetTreeChildrenCascader(IList<Organization> entities,
            ICollection<CascaderSelectViewModel> results,
            string? parentId)
        {
            IList<Organization> result = string.IsNullOrEmpty(parentId)
                ? entities.Where(p => string.IsNullOrEmpty(p.ParentId)).ToList()
                : entities.Where(p => p.ParentId == parentId).ToList();
            var organizationUser = GetUserByOrganizationIds(result.Select(c => c.Id!).ToList());
            foreach (var t1 in result)
            {
                var list =
                    organizationUser.Where(c => c.OrganizationId == t1.Id)
                        .Select(c => new CascaderSelectViewModel
                        {
                            Key = c.Key,
                            Label = c.Label,
                            Value = c.Value,
                            Disabled = c.Disabled,
                            Category = 1
                        }).ToList();
                var res = new CascaderSelectViewModel
                {
                    Key = t1.Id!,
                    ParentId = t1.ParentId,
                    Label = t1.Name,
                    Value = t1.Id!,
                    Disabled = list.Count <= 0,
                    Children = list,
                    Category = 2
                };
                if (entities.Any(p => p.ParentId == t1.Id))
                {
                    GetTreeChildrenCascader(entities, res.Children, t1.Id);
                }

                res.Disabled = res.Children.Count <= 0;
                results.Add(res);
            }
        }

        /// <summary>
        /// 递归读取
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="results"></param>
        /// <param name="orgId"></param>
        /// <param name="parentId"></param>
        /// <param name="existList"></param>
        private void GetTreeChildrenCascaderMore(IList<Organization> entities,
            ICollection<CascaderSelectViewModel> results,
            string? orgId, string? parentId, List<string> existList)
        {
            IList<Organization> result = !string.IsNullOrEmpty(orgId)
                ? entities.Where(p => p.Id == orgId).ToList()
                : entities.Where(p => p.ParentId == parentId).ToList();

            var organizationUser = GetUserByOrganizationIds(result.Select(c => c.Id).ToList()!);
            foreach (var t1 in result)
            {
                if (existList.Any(c => c == t1.Id))
                {
                    continue;
                }

                var list =
                    organizationUser.Where(c => c.OrganizationId == t1.Id)
                        .Select(c => new CascaderSelectViewModel
                        {
                            Key = c.Key,
                            Label = c.Label,
                            Value = c.Value,
                            Disabled = c.Disabled,
                            Category = 1
                        }).ToList();
                var res = new CascaderSelectViewModel
                {
                    Key = t1.Id!,
                    ParentId = t1.ParentId,
                    Label = t1.Name,
                    Value = t1.Id!,
                    Disabled = list.Count <= 0,
                    Children = list,
                    Category = 2
                };
                existList.Add(t1.Id!);
                if (entities.Any(p => p.ParentId == t1.Id))
                {
                    GetTreeChildrenCascaderMore(entities, res.Children, "", t1.Id, existList);
                }

                res.Disabled = res.Children.Count <= 0;
                results.Add(res);
            }
        }


        /// <summary>
        /// 递归读取（下属）
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="results"></param>
        /// <param name="myOrganization"></param>
        /// <param name="parentId"></param>
        private void GetTreeChildrenSubordinate(IList<Organization> entities,
            ICollection<CascaderSelectViewModel> results, ISugarQueryable<string> myOrganization,
            string? parentId)
        {
            IList<Organization> result = string.IsNullOrEmpty(parentId)
                ? entities.Where(p => string.IsNullOrEmpty(p.ParentId)).ToList()
                : entities.Where(p => p.ParentId == parentId).ToList();
            var organizationUser = GetUserByOrganizationIds(entities.Select(c => c.Id!).ToList(), myOrganization, true);
            foreach (var t1 in result)
            {
                var list =
                    organizationUser.Where(c => c.OrganizationId == t1.Id)
                        .Select(c => new CascaderSelectViewModel
                        {
                            Key = c.Key,
                            Label = c.Label,
                            Value = c.Value,
                            Disabled = c.Disabled
                        }).ToList();
                var res = new CascaderSelectViewModel
                {
                    Key = ObjectId.GenerateNewStringId(),
                    ParentId = t1.ParentId,
                    Label = t1.Name,
                    Value = t1.Id!,
                    Disabled = !list.Any(),
                    Children = list
                };
                if (entities.Any(p => p.ParentId == t1.Id))
                {
                    GetTreeChildrenSubordinateChild(entities, res.Children, organizationUser, t1.Id);
                }

                results.Add(res);
            }
        }

        private void GetTreeChildrenSubordinateChild(IList<Organization> entities,
            ICollection<CascaderSelectViewModel> results,
            List<CascaderSelectViewOrganizationModel> organizationUser,
            string? parentId)
        {
            IList<Organization> result = string.IsNullOrEmpty(parentId)
                ? entities.Where(p => string.IsNullOrEmpty(p.ParentId)).ToList()
                : entities.Where(p => p.ParentId == parentId).ToList();
            foreach (var t1 in result)
            {
                var list =
                    organizationUser.Where(c => c.OrganizationId == t1.Id)
                        .Select(c => new CascaderSelectViewModel
                        {
                            Key = c.Key,
                            Label = c.Label,
                            Value = c.Value,
                            Disabled = c.Disabled,
                            Isleaf = true
                        }).ToList();
                var res = new CascaderSelectViewModel
                {
                    Key = ObjectId.GenerateNewStringId(),
                    ParentId = t1.ParentId,
                    Label = t1.Name,
                    Value = t1.Id!,
                    Isleaf = false,
                    Disabled = !list.Any(),
                    Children = list
                };
                if (entities.Any(p => p.ParentId == t1.Id))
                {
                    GetTreeChildrenSubordinateChild(entities, res.Children, organizationUser, t1.Id);
                }

                results.Add(res);
            }
        }

        /// <summary>
        /// 通过组织架构Ids获取当前的人员
        /// </summary>
        /// <param name="ids"></param>
        /// <param name="myOrganization">我所在的组织架构</param>
        /// <param name="isSubordinate">是否是下属</param>
        /// <returns></returns>
        private List<CascaderSelectViewOrganizationModel> GetUserByOrganizationIds(List<string> ids,
            ISugarQueryable<string>? myOrganization = null,
            bool isSubordinate = false)
        {
            if (isSubordinate && myOrganization == null)
            {
                throw new CoreException("必须传递【myOrganization】参数");
            }

            var list = QueryNoTracking<OrganizationUser>()
                .HasWhere(isSubordinate, n => GetSubordinate(myOrganization!) == n.UserId)
                .Where(p => ids.Contains(p.OrganizationId))
                .Select(p => new CascaderSelectViewOrganizationModel
                {
                    Key = p.Id,
                    Disabled = false,
                    Label = p.User.RealName,
                    Value = p.User.Id!,
                    OrganizationId = p.OrganizationId
                }).ToList();

            return list;
        }

        /// <summary>
        /// 递归读取
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="results"></param>
        /// <param name="parentId"></param>
        private static void GetTreeChildrenBySelect(IList<Organization> entities,
            ICollection<TreeSelectViewModel> results,
            string? parentId)
        {
            IList<Organization> result = string.IsNullOrEmpty(parentId)
                ? entities.Where(p => string.IsNullOrEmpty(p.ParentId)).ToList()
                : entities.Where(p => p.ParentId == parentId).ToList();

            foreach (var t1 in result)
            {
                var res = new TreeSelectViewModel
                {
                    Id = t1.Id,
                    ParentId = t1.ParentId,
                    Title = t1.Name,
                    Value = t1.Id,
                    Key = t1.Id,
                    Disabled = t1.Status == Status.Disabled,
                    IsLeaf = !string.IsNullOrEmpty(t1.ParentId)
                };
                if (entities.Any(p => p.ParentId == t1.Id))
                {
                    res.Children = new List<TreeSelectViewModel>();
                    GetTreeChildrenBySelect(entities, res.Children, t1.Id);
                }

                results.Add(res);
            }
        }

        /// <summary>
        /// 递归读取
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="results"></param>
        /// <param name="parentId"></param>
        private static void GetTreeChildren(IList<Organization> entities,
            ICollection<GetOrganizationTreeDataResponse> results,
            string? parentId)
        {
            IList<Organization> result = string.IsNullOrEmpty(parentId)
                ? entities.Where(p => string.IsNullOrEmpty(p.ParentId)).ToList()
                : entities.Where(p => p.ParentId == parentId).ToList();

            foreach (var item in result)
            {
                var res = new GetOrganizationTreeDataResponse
                {
                    Id = item.Id!,
                    ParentId = item.ParentId,
                    Name = item.Name,
                    ContactName = item.ContactName,
                    ContactPhoneNumber = item.ContactPhoneNumber,
                    Remarks = item.Remarks,
                    Created = item.Created,
                    Updated = item.Updated,
                    Status = item.Status,
                    CreatorId = item.CreatorId
                };
                if (entities.Any(p => p.ParentId == item.Id))
                {
                    res.Children = new List<GetOrganizationTreeDataResponse>();
                    GetTreeChildren(entities, res.Children, item.Id);
                }

                results.Add(res);
            }
        }

        #endregion

        #region 扩展信息

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<GetOrganizationExtendByIdResponse> GetExtendByIdAsync(string id)
        {
            var result = await QueryNoTracking()
                .Where(p => p.Id == id)
                .Select(p => new GetOrganizationExtendByIdResponse
                {
                    Id = p.Id!,
                    ContactName = p.ContactName,
                    ContactPhoneNumber = p.ContactPhoneNumber,
                    Longitude = p.Longitude,
                    Latitude = p.Latitude,
                    Address = p.Address,
                
                    ExtendRemarks = p.ExtendRemarks,
                })
                .FirstOrDefaultAsync();

            return result;
        }

        #endregion

        #region 计算两个经纬度的距离

        /// <summary>
        /// 计算两点位置的距离，返回两点的距离，单位 米
        /// 该公式为GOOGLE提供，误差小于0.2米
        /// </summary>
        /// <param name="lat1">第一点纬度</param>
        /// <param name="lng1">第一点经度</param>
        /// <param name="lat2">第二点纬度</param>
        /// <param name="lng2">第二点经度</param>
        /// <returns></returns>
        public static double GetDistance(double lat1, double lng1, double lat2, double lng2)
        {
            double radLat1 = Rad(lat1);
            double radLng1 = Rad(lng1);
            double radLat2 = Rad(lat2);
            double radLng2 = Rad(lng2);
            double a = radLat1 - radLat2;
            double b = radLng1 - radLng2;
            double result = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a / 2), 2) +
                                                    Math.Cos(radLat1) * Math.Cos(radLat2) *
                                                    Math.Pow(Math.Sin(b / 2), 2))) * EARTH_RADIUS;
            return result;
        }

        /// <summary>
        /// 经纬度转化成弧度
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        private static double Rad(double d)
        {
            return (double)d * Math.PI / 180d;
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dbContext"></param>
        /// <param name="securityContextAccessor"></param>
        /// <param name="configuration"></param>
        /// <param name="cacheManager"></param>
        /// <param name="dataPermission"></param>
        public OrganizationService(ISqlSugarClient dbContext, ISecurityContextAccessor securityContextAccessor,
            IConfiguration? configuration, ICacheManager cacheManager, IDataPermission dataPermission) : base(dbContext,
            securityContextAccessor, configuration, cacheManager, dataPermission)
        {
        }
    }
}